package com.hc.wego.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.TimeZone;

/**
 * JSON工具类
 *
 * @author hc
 */
public class JsonUtil {

    /**
     * 默认日期时间格式
     */
    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 默认日期格式
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 默认时间格式
     */
    private static final String TIME_FORMAT = "HH:mm:ss";
    /**
     * 创建ObjectMapper实例，用于JSON处理
     */
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    // Json序列化和反序列化转换器
    static {
        //java8日期 Local系列序列化和反序列化模块
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //序列化
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
        //反序列化
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));

        // 注册模块以支持序列化和反序列化
        OBJECT_MAPPER.registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(javaTimeModule);

        // 禁用写日期为时间戳的形式
        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 禁用调整日期到上下文时区
        OBJECT_MAPPER.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        // 忽略json字符串中不识别的属性
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 忽略无法转换的对象
        OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        //// PrettyPrinter 格式化输出
        //OBJECT_MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true);
        // NULL不参与序列化
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 指定时区
        OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        // 全局日期格式设置
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(DATE_TIME_FORMAT));
    }

    /**
     * 将对象转换成字符串
     * @param obj 要转换的对象
     * @param <T> 泛型参数
     * @return 转换后的字符串，如果出现异常则返回null
     */
    public static <T> String obj2string(T obj) {
        if (obj == null) {
            return null;
        }
        String s = null;
        try {
            if (obj instanceof String res) {
                return res;
            }
            s = OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return s;
    }

    /**
     * 将对象转换成格式化后的字符串
     * @param obj 要转换的对象
     * @param <T> 泛型参数
     * @return 格式化后的字符串，如果出现异常则返回null
     */
    public static <T> String obj2stringPretty(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            if (obj instanceof String res) {
                return res;
            }
            OBJECT_MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true);
            return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    /**
     * 字符串转对象
     * @param str 要转换的字符串
     * @param clazz 目标对象的类
     * @param <T> 泛型参数
     * @return 转换后的对象，如果出现异常则返回null
     */
    public static <T> T string2obj(String str, Class<T> clazz) {
        if (str == null || str.length() == 0 || clazz == null) {
            return null;
        }
        T t = null;
        try {
            t = clazz.equals(String.class) ? (T) str : OBJECT_MAPPER.readValue(str, clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 在字符串与集合对象转换时使用
     * @param str 要转换的字符串
     * @param typeReference 目标类型的引用
     * @param <T> 泛型参数
     * @return 转换后的对象，如果出现异常则返回null
     */
    public static <T> T string2obj(String str, TypeReference<T> typeReference) {
        if (str == null || str.length() == 0 || typeReference == null) {
            return null;
        }
        try {
            return (T) (typeReference.getType().equals(String.class) ? str : OBJECT_MAPPER.readValue(str, typeReference));
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 在字符串与集合对象转换时使用
     * @param str 要转换的字符串
     * @param collectionClazz 集合类
     * @param elementClazz 集合元素类
     * @param <T> 泛型参数
     * @return 转换后的对象，如果出现异常则返回null
     */
    public static <T> T string2obj(String str, Class<?> collectionClazz, Class<?>... elementClazz) {
        JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(collectionClazz, elementClazz);
        try {
            return OBJECT_MAPPER.readValue(str, javaType);
        } catch (IOException e) {
            return null;
        }
    }

    public static void main(String[] args) {
        final Map<Object, Object> map = Map.of("id", 1234, "name", "张三", "age", 18, "birth", LocalDate.of(1999, 1, 1), "married", false);
        System.out.println(obj2string(map));
        System.out.println(obj2stringPretty(map));
    }
}

